//
//  Analyzer.cpp
//  RoutingEvaluator
//
//  Created by Christopher Kemsley on 4/15/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include "Analyzer.hpp"
#import "IAGraphics.h"


NamespaceBegin



std::map<std::string,Analyzer*> const&	Analyzer :: analyzers ()
	{
	return mAnalyzers() ;
	}
Analyzer*	Analyzer :: analyzer ( std::string const& name )
	{
	std::map<std::string,Analyzer*>::iterator found = mAnalyzers() . find ( name ) ;
	if ( found == mAnalyzers().end() )
		return 0 ;
	return found->second ;
	}




Analyzer :: Analyzer ( char const* name )
	{
	mAnalyzers()[name] = this ;
	}
Analyzer :: ~Analyzer ()
	{
	
	}


std::map<std::string,Analyzer*>&	Analyzer :: mAnalyzers ()
	{
	static std::map<std::string,Analyzer*> s ;
	return s ;
	}









Output :: Output ( std::string const & basePath )
:	mBasePath	( basePath )
,	mLogOut		( (basePath+", Log.txt").c_str() , std::ios_base::out )
,	mTableOut	( (basePath+", Table.txt").c_str() , std::ios_base::out )
	{
	if ( ! mLogOut.is_open() )
		ThrowException ( "Output" , "Failed to open log output" , __FILE__,__LINE__ ) ;
	if ( ! mTableOut.is_open() )
		ThrowException ( "Output" , "Failed to open table output" , __FILE__,__LINE__ ) ;
	
	mLogOut << "Base Path: " << basePath << "\n" ;
	NSString * start = [NSString stringWithFormat:@"Run Date: %@",[NSDate date]] ;
	mLogOut << [start cStringUsingEncoding:NSASCIIStringEncoding] << "\n\n" ;
	}
Output :: ~Output ()
	{
	mLogOut.close() ;
	mTableOut.close() ;
	}


std::ostream&	Output :: logOut ()
	{
	return mLogOut ;
	}
std::ostream&	Output :: tableOut ()
	{
	return mTableOut ;
	}

void	Output :: log		( NSString * s )
	{
	mLogOut << [s cStringUsingEncoding:NSASCIIStringEncoding] << "\n" ;
	}
void	Output :: log		( NSString * format , ... )
	{
	va_list l ;
	va_start(l, format) ;
	
	NSString * s = [ [NSString alloc] initWithFormat:format arguments:l ] ;
	mLogOut << [s cStringUsingEncoding:NSASCIIStringEncoding] << "\n" ;
	
	va_end(l) ;
	}


void	Output :: drawNetworkTopology	( NodeSet * nodes , LinkVector const & links )
	{
	// Output image size
	CGSize			size			=	{ 1024 , 1024 } ;
	static CGFloat	nodeRadius		=	10 ;
	static CGFloat	textSize		=	20 ;
	static CGFloat	nodeColor[]		=	{ 0 , 0 , 1 , 1 } ;
	static CGFloat	linkColor[]		=	{ 0 , 1 , 0 , 1 } ;
	static CGFloat	frameColor[]	=	{ 0.95 , 0.95 , 1 , 1 } ;
	
	static CGFloat	labelColor[]	=	{ 0 , 0 , 1 , 1 } ;
	static CGFloat	costColor[]		=	{ 1 , 0 , 0 , 1 } ;
	
	mLogOut << "*** Image drawer: starting ***\n" ;
	
	
	/* Get the bounds */
	
	CGRect	bounds	=	CGRectZero ;
		{
		std::vector<Node*>::const_iterator	here	=	nodes->nodes().begin() ;
		std::vector<Node*>::const_iterator	end		=	nodes->nodes().end() ;
		
		Node * node = *(here++) ;
		bounds.origin.x		=	node->identity->location.dX ;
		bounds.origin.y		=	node->identity->location.dY ;
		
		while ( here < end )
			{
			node = *(here++) ;
			CGFloat	x	=	node->identity->location.dX ;
			CGFloat	y	=	node->identity->location.dY ;
			
			if ( x < bounds.origin.x )
				bounds.origin.x = x ;
			if ( y < bounds.origin.y )
				bounds.origin.y = y ;
			
			if ( x > (bounds.origin.x+bounds.size.width) )
				bounds.size.width = (x-bounds.origin.x) ;
			if ( y > (bounds.origin.y+bounds.size.height) )
				bounds.size.height = (y-bounds.origin.y) ;
			}
		
		// Make a 7% margin on all sides
		CGFloat dX = bounds.size.width * 0.07 ;
		CGFloat dY = bounds.size.height * 0.07 ;
		bounds = CGRectInset ( bounds , -dX , -dY ) ;
		
		// Change the size to the same aspect ratio
		CGFloat	heightOverWidth	=	bounds.size.height / bounds.size.width ;
		size.height = heightOverWidth * size.width ;
		}
	
	
	
	// Maps spacial location to pixel point
	CGPoint	(^pointForLocation) ( Vector2D const& ) = ^CGPoint(Vector2D const& location){
		CGFloat	pX	=	(location.dX-bounds.origin.x) / bounds.size.width ;
		CGFloat	pY	=	(location.dY-bounds.origin.y) / bounds.size.height ;
		CGPoint	pt	=	{ pX*size.width , pY*size.height } ;
		return pt ;
	} ;
	CGPoint	(^pointBetweenLocations) ( Vector2D const& , Vector2D const& ) = ^CGPoint(Vector2D const& l1,Vector2D const& l2){
		CGPoint	p1	=	pointForLocation ( l1 ) ;
		CGPoint	p2	=	pointForLocation ( l2 ) ;
		p1.x	+=	(p2.x-p1.x)*0.5 ;
		p1.y	+=	(p2.y-p1.y)*0.5 ;
		return p1 ;
	} ;
	
	
	
	
	CGMutablePathRef	nodePath	=	CGPathCreateMutable() ;
	CGMutablePathRef	linkPath	=	CGPathCreateMutable() ;
	CGMutablePathRef	framePath	=	CGPathCreateMutable() ;
	
	
	
	
	
	/* Add the nodes */
		{
		std::vector<Node*>::const_iterator	here	=	nodes->nodes().begin() ;
		std::vector<Node*>::const_iterator	end		=	nodes->nodes().end() ;
		while ( here < end )
			{
			Node *	node	=	*(here++) ;
			
			CGPoint	point	=	pointForLocation ( node->identity->location ) ;
			
			CGPathAddCircleAtPoint ( nodePath,0 , point , nodeRadius ) ;
			}
		}
	
	
	
	/* Add the links */
		{
		std::vector<Link*>::const_iterator	here	=	links.begin() ;
		std::vector<Link*>::const_iterator	end		=	links.end() ;
		while ( here < end )
			{
			Link *	link	=	*(here++) ;
			
			CGPoint	point1	=	pointForLocation ( link->identity->endpoint1->location ) ;
			CGPoint	point2	=	pointForLocation ( link->identity->endpoint2->location ) ;
			
			CGPathAddLineBetweenPoints ( linkPath,0 , point1 , point2 ) ;
			}
		}
	
	
	
	/* Make the frame */
	CGRect frame = { CGPointZero , size } ;
	CGPathAddRoundedRect (  framePath,0 , frame , 10 ) ;
	
	
	
	
	
	/* Make the image */
	CGSize imageSize = { size.width+nodeRadius , size.height+nodeRadius } ; // extra space for labels
	CGImageRef	image	=	CGImageCreateWithDrawingBlock ( imageSize , ^(CGContextRef context){
		CGContextAddPath ( context , framePath ) ;
		CGContextSetFillColor ( context , frameColor ) ;
		CGContextFillPath(context) ;
		
		CGContextAddPath ( context , linkPath ) ;
		CGContextSetStrokeColor ( context , linkColor ) ;
		CGContextStrokePath(context) ;
		
		CGContextAddPath ( context , nodePath ) ;
		CGContextSetFillColor ( context , nodeColor ) ;
		CGContextFillPath(context) ;
		
		
		
		/* Add the node titles */
		{
		CGContextSelectFont ( context , "Verdana" , textSize , kCGEncodingMacRoman ) ;
		CGContextSetFillColor ( context , labelColor ) ;
		
		std::vector<Node*>::const_iterator	here	=	nodes->nodes().begin() ;
		std::vector<Node*>::const_iterator	end		=	nodes->nodes().end() ;
		while ( here < end )
			{
			Node *	node	=	*(here++) ;
			
			CGPoint	point	=	pointForLocation ( node->identity->location ) ;
			point.x += nodeRadius ;
			point.y += nodeRadius ;
			
			char str [50] ;
			sprintf ( str , "%u" , node->identity->identifier ) ;
			
			CGContextShowTextAtPoint ( context , point.x,point.y , str , strlen(str) ) ;
			}
		}
		
		// Add the link costs
		{
		CGContextSelectFont ( context , "Arial" , textSize , kCGEncodingMacRoman ) ;
		CGContextSetFillColor ( context , costColor ) ;
		
		LinkVector::const_iterator	here	=	links.begin() ;
		LinkVector::const_iterator	end		=	links.end() ;
		while ( here < end )
			{
			Link *	link	=	*(here++) ;
			
			CGPoint	point	=	pointBetweenLocations ( link->endpoint1->identity->location , link->endpoint2->identity->location ) ;
			point.x -= nodeRadius ;
			point.y -= nodeRadius ;
			
			char str [50] ;
			sprintf ( str , "%.0lf" , link->identity->cost() ) ;
			
			CGContextShowTextAtPoint ( context , point.x,point.y , str , strlen(str) ) ;
			}
		}
	}) ;
	
	
	
	
	
	/* Save the image */
	NSString	*	basePath	=	[NSString stringWithCString:mBasePath.c_str() encoding:NSASCIIStringEncoding] ;
	NSString	*	imagePath	=	[basePath stringByAppendingString:@", Map.png"] ;
	CGImageWritePNGToFile ( image , imagePath ) ;
	
	
	
	
	
	CGImageRelease(image) ;
	CGPathRelease(framePath) ;
	CGPathRelease(linkPath) ;
	CGPathRelease(nodePath) ;
	
	
	
	mLogOut << "*** Image drawer: complete ***\n" ;
	}





NamespaceEnd
